package in.co.codedoc.json;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

public class JSONObjectValue
	extends JSONValue
{
	public JSONObjectValue()
	{
		this(new HashMap<JSONValue, JSONValue>());
	}
	
	public JSONObjectValue(HashMap<JSONValue, JSONValue> properties)
	{
		this.valueByKey = properties;
	}

	public JSONObjectValue(JSONValue jsonValue)
	{
		if(jsonValue == null)
		{
			this.valueByKey = new HashMap<JSONValue, JSONValue>();
		}
		else if(jsonValue instanceof JSONObjectValue)
		{
			this.valueByKey = ((JSONObjectValue)jsonValue).valueByKey;
		}
		else
		{
			throw new RuntimeException("Dont know how to make a JSONObjectValue from " + jsonValue.getClass());
		}
	}
	
	public JSONValue GetProperty(JSONValue key)
	{
		return valueByKey.get(key);
	}

	public void SetProperty(String key,JSONValue value)
	{
		SetProperty(new JSONStringValue(key),value);
	}
	
	public void SetProperty(JSONValue key,JSONValue value)
	{
		valueByKey.put(key, value);
		if(valueByStringKey != null)
		{
			valueByStringKey.put(key.Encode(), value);
		}
	}

	public boolean HasProperty(String key)
	{
		InitializeValueByStringKey();
		
		return valueByStringKey.containsKey(key);
	}
	
	public JSONValue GetProperty(String key)
	{
		InitializeValueByStringKey();
		
		return valueByStringKey.get(key);
	}
	
	private void InitializeValueByStringKey()
	{
		if(valueByStringKey == null)
		{
			synchronized(this)
			{
				if(valueByStringKey == null)
				{
					valueByStringKey = new HashMap<String, JSONValue>();
				
					ForEach
					(
						new ForEach<Object>()
						{
							public void Do(int index,JSONValue k,JSONValue v)
							{
								valueByStringKey.put(k instanceof JSONStringValue ? ((JSONStringValue)k).GetValue() : k.Encode(),v);
							}
						}
					);
				}
			}
		}		
	}

	@SuppressWarnings("unchecked")
	public Collection<String> GetPropertyNames(Collection<String> propertyNames)
	{
		InitializeValueByStringKey();
		
		if(propertyNames == null)
		{
			propertyNames = new ArrayList<String>();
		}
		
		propertyNames.addAll(valueByStringKey.keySet());
		
		if(propertyNames instanceof List)
		{
			Collections.sort((List)propertyNames);
		}
		
		return propertyNames;
	}
	
	public static class ForEach<T>
	{
		public void Do(int index,JSONValue key,JSONValue value)
		{
			
		}
		
		public T Do(int index,JSONValue key,JSONValue value,T t)
		{
			return t;
		}
	}
	
	public <T> void ForEach(ForEach<T> fe)
	{
		int index = 0;
		for(JSONValue k:valueByKey.keySet())
		{
			fe.Do(index++, k, valueByKey.get(k));
		}
	}

	public <T> T ForEach(ForEach<T> fe,T t)
	{
		int index = 0;
		for(JSONValue k:valueByKey.keySet())
		{
			t = fe.Do(index++, k, valueByKey.get(k),t);
		}
		
		return t;
	}
	
	@Override
	public StringBuffer Encode(final StringBuffer buffer)
	{
		buffer.append('{');
		
		ForEach
		(
			new ForEach<Object>()
			{
				public void Do(int index,JSONValue k,JSONValue v)
				{
					if(index > 0)
					{
						buffer.append(',');
					}
					k.Encode(buffer);
					buffer.append(':');
					v.Encode(buffer);					
				}
			}
		);
		
		buffer.append('}');
		
		return buffer;
	}
	
	@Override
	public boolean equals(Object t)
	{
		if((t == null) || !(t instanceof JSONObjectValue))
		{
			return false;
		}

		JSONObjectValue that = (JSONObjectValue)t;
		
		return that.valueByKey == valueByKey;
	}

	@Override
	public int hashCode()
	{
		return valueByKey.hashCode();
	}
	
	private HashMap<JSONValue, JSONValue> valueByKey;
	private HashMap<String, JSONValue> valueByStringKey;
}
